
#include <librally.h>

#ifdef _PTHREAD

Err rp_rwlock_init(rp_rwlock_t * rwlock)
{
	static char fname[] = "rp_rwlock_init";
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_init(rwlock, NULL);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot initialize the R/W lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_init_pshared(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_init_pshared";
	static char msg[] = "fatal error";
	Err err;
	pthread_rwlockattr_t attr;
	pthread_rwlockattr_t *p = &attr;
	int ret;
	FLOG_IN(fname);

	if (pthread_rwlockattr_init(&attr) || pthread_rwlockattr_setpshared(&attr))
	{
		rp_log(LOG_CRITICAL, fname, "Cannot set the R/W lock to shared mode, use Private mode instead.");
		p = NULL;
	}

	ret = pthread_rwlock_init(rwlock, p);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot initialize the R/W lock. Error: %d", ret);
		return E_ERROR;
	}


	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_destroy(rp_rwlock_t * rwlock)
{
	static char fname[] = "rp_rwlock_destroy";
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_destroy(rwlock);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot destory the R/W lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_rdlock(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_rdlock"
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_rdlock(rwlock);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot get the Read lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_tryrdlock(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_tryrdlock"
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_tryrdlock(rwlock);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot try to get the Read lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_wrlock(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_wrlock"
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_wrlock(rwlock);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot get the Write lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_trywrlock(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_trywrlock"
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_trywrlock(rwlock);	
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot try to get the Write lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_unlock(rp_rwlock_t *rwlock)
{
	static char fname[] = "rp_rwlock_unlock"
	static char msg[] = "fatal error";
	Err err;
	int ret;
	FLOG_IN(fname);

	ret = pthread_rwlock_unlock(rwlock);
	if (ret)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot release R/W lock. Error: %d", ret);
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

#else	//=====================================================================

Err rp_rwlock_init(rp_rwlock_t * rwlock)
{
	static char fname[] = "rp_rwlock_init";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);
	
	if (rp_mutex_init(&(rwlock->mutex), NULL) != E_OK ||
	     rp_cond_init(&(rwlock->cond_read), NULL) != E_OK ||
	     rp_cond_init(&(rwlock->cond_write), NULL) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot initialize the R/W lock.");
		return E_ERROR;
	}
	rwlock->num_read = 0;
	rwlock->num_write = 0;
	rwlock->wait_read = 0;
	rwlock->wait_write = 0;

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_destroy(rp_rwlock_t * lock)
{
	static char fname[] = "rp_rwlock_destroy";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);
	
	if (rp_mutex_destroy(&(lock->mutex)) != E_OK ||
	     rp_cond_destroy(&(lock->cond_read)) != E_OK ||
	     rp_cond_destroy(&(lock->cond_write)) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot destroy the R/W lock.");
		return E_ERROR;
	}

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_rdlock(rp_rwlock_t * lock)
{
	static char fname[] = "rp_rwlock_rdlock";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (rp_mutex_lock(&(lock->mutex)) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot get the Read lock.");
		return E_ERROR;
	}
	
	while (lock->num_write || lock->wait_write)
	{
		lock->wait_read++;
		if (rp_cond_wait(&(lock->cond_read), &(lock->mutex)) != E_OK)
		{
			lock->wait_read--;
			err = rp_mutex_unlock(&(lock->mutex));
			rp_log(LOG_CRITICAL, fname, "Cannot get the Read lock.");
			return E_ERROR;
		}
		lock->wait_read--;
	} 
	lock->num_read++;	
	err = rp_mutex_unlock(&(lock->mutex));

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_rdunlock(rp_rwlock_t * lock)
{
	static char fname[] = "rp_rwlock_rdlock";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (rp_mutex_lock(&(lock->mutex)) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot release the Read lock.");
		return E_ERROR;
	}
	
	lock->num_read--;
	if (!lock->num_read && lock->wait_write)
	{	
		rp_log(LOG_DEBUG, fname, "Wakeup one of the pending writes.");
		err = rp_cond_signal(&(lock->cond_write));
	}
	err = rp_mutex_unlock(&(lock->mutex));

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_wrlock(rp_rwlock_t * lock)
{
	static char fname[] = "rp_rwlock_wrlock";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (rp_mutex_lock(&(lock->mutex)) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot get the Write lock.");
		return E_ERROR;
	}
	
	while (lock->num_read || lock->num_write)
	{
		lock->wait_write++;
		if (rp_cond_wait(&(lock->cond_write), &(lock->mutex)) != E_OK)
		{
			lock->wait_write--;
			err = rp_mutex_unlock(&(lock->mutex));
			rp_log(LOG_CRITICAL, fname, "Cannot get the Write lock.");
			return E_ERROR;
		}
		lock->wait_write--;
	}
	lock->num_write++;
	err = rp_mutex_unlock(&(lock->mutex));

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rwlock_wrunlock(rp_rwlock_t * lock)
{
	static char fname[] = "rp_rwlock_wrlock";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (rp_mutex_lock(&(lock->mutex)) != E_OK)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot release the Write lock.");
		return E_ERROR;
	}
	
	lock->num_write--;
	if (lock->wait_write)
	{
		rp_log(LOG_DEBUG, fname, "Wakeup one of the pending writers.");
		rp_cond_signal(&(lock->cond_write));
	} else if (lock->wait_read)
	{
		rp_log(LOG_DEBUG, fname, "Wakeup all pending readers.");
		rp_cond_broadcast(&(lock->cond_read));
	}
	err = rp_mutex_unlock(&(lock->mutex));

	FLOG_OUT(fname);
	return E_OK;
}

#endif	// _PTHREAD

